home *** CD-ROM | disk | FTP | other *** search
- /*----------------------------------------------------------------------------
-
- ftp.c
-
- This reusable and reentrant module implements FTP operations.
-
- The following functions are exported:
-
- FtpOpen - Open an FTP stream.
- FtpClose - Close an FTP stream.
- FtpGetFile - Get a file.
- FtpGetListing - Get a directory listing.
- FtpPutFile - Put a file.
- FtpDeleteFile - Delete a file.
- FtpGetServerErrInfo - Get server error information.
- FtpNumBytesTransferred - Get number of bytes transferred so far.
-
- You must call memutil.c/InitMemUtil and net.c/NetInit before calling any of
- the functions in this module. You also must call the NetIdle function in your
- idle loop, and the NetTerm function at program termination.
-
- A "stream" is an abstraction representing a bidirectional network connection
- to an FTP server. A stream is represented as a variable of type "FtpStreamRef".
- These stream references are opaque. You may copy them and pass them as parameters
- to functions in ftp.c, but you are prohibited from accessing the contents of
- the memory blocks pointed to by the references. Only the functions in
- ftp.c are permitted to manipulate the contents of these blocks.
-
- The functions return a value of type OSErr as the function result:
-
- noErr no error occurred
- ftpServerErr server error
- other any other OS or Toolbox error code
-
- If the function result is ftpServerErr, the FtpGetServerErrInfo function can
- be called to get information about the server error. On server errors, the
- stream is still allocated and open on return to the caller.
-
- If an OS or Toolbox error occurs, the stream is aborted and deallocated before
- returning to the caller. "Aborted" means that the server connection is closed
- abruptly, without going through the usual TCP stream teardown process. You must
- perform careful error checking. The stream is deallocated, and must not be reused.
-
- Copyright © 1994-1995, Northwestern University.
-
- ----------------------------------------------------------------------------*/
-
- #include <stdlib.h>
- #include <string.h>
- #include <stdio.h>
-
- #include "def.h"
- #include "net.h"
- #include "ftp.h"
- #include "memutil.h"
-
-
-
- typedef struct TStream {
- NetStreamRef netStream; /* net stream reference for control stream */
- unsigned long addr; /* address of server */
- Boolean textMode; /* true if stream currently in text mode */
- Boolean passive; /* true to use passive mode */
- NetStreamRef dataStream; /* net stream reference for data stream, or
- nil if no data stream open */
- Boolean get; /* true if get, false if put, undefined if
- no data stream open */
- } TStream, *TStreamPtr, **TStreamHandle;
-
-
-
- /*----------------------------------------------------------------------------
- FtpOpen
-
- Open an FTP stream.
-
- Entry: host = server host address (domain name or dotted
- decimal IP address).
- username = user name.
- password = password.
- passive = true to use passive mode FTP (recommended
- if more than one file is to be transferred).
-
- Exit: function result = result code.
- stream = reference to opened stream.
-
- In passive mode, the PASV command is used on all data transfers to
- tell the FTP server to open a passive listening data stream for the
- transfer using a unique port number. The FTP server sends a response
- to the PASV command telling the Mac the port number to use. The Mac
- connects to this port to establish the data stream connection.
-
- In active mode, the Mac establishes the listening data stream for
- the transfer and uses the PORT command to tell the FTP server the
- port number. The FTP server connects to this port to establish
- the data stream connection.
-
- When transferring more than one file using a single control stream,
- passive mode is recommended. In active mode, with some kinds of
- servers, there can sometimes be significant delays (30-90 seconds)
- in establishing the data stream connection.
- ----------------------------------------------------------------------------*/
-
- OSErr FtpOpen (char *host, char *username, char *password, Boolean passive,
- FtpStreamRef *stream)
- {
- TStreamHandle s = nil;
- unsigned long addr;
- unsigned short port;
- NetStreamRef netStream;
- CStr255 command, response;
- long responseCode;
- OSErr err = noErr;
-
- err = MyNewHandle(sizeof(TStream), &s);
- if (err != noErr) return err;
-
- err = NetNameToAddr(host, kFTPPort, &addr, &port);
- if (err != noErr) goto exit;
-
- err = NetOpen(addr, port, true, &netStream, &responseCode, response);
- if (err != noErr) goto exit;
-
- (**s).netStream = netStream;
- (**s).addr = addr;
- (**s).textMode = true;
- (**s).passive = passive;
- *stream = (FtpStreamRef)s;
-
- if (responseCode != 220) return ftpServerErr;
-
- sprintf(command, "USER %.250s", username);
- err = NetCommand(netStream, command, &responseCode, response);
- if (err != noErr) goto exit;
- if (responseCode != 331) return ftpServerErr;
-
- sprintf(command, "PASS %.250s", password);
- err = NetCommand(netStream, command, &responseCode, response);
- if (err != noErr) goto exit;
- if (responseCode != 230) return ftpServerErr;
-
- return noErr;
-
- exit:
-
- MyDisposeHandle(s);
- return err;
- }
-
-
-
- /*----------------------------------------------------------------------------
- FtpClose
-
- Close an FTP stream.
-
- Entry: stream = stream reference.
-
- Exit: function result = result code.
- ----------------------------------------------------------------------------*/
-
- OSErr FtpClose (FtpStreamRef stream)
- {
- TStreamHandle s;
- OSErr err = noErr;
-
- s = (TStreamHandle)stream;
- err = NetClose((**s).netStream);
- MyDisposeHandle(s);
- return err;
- }
-
-
-
- /*----------------------------------------------------------------------------
- GetOrPutFileOrListing
-
- Get or put a file or a listing from or to an FTP server.
-
- Entry: stream = stream reference.
- path = file path.
- cmd = "RETR", "LIST", or "STOR".
- get = true if get operation, false if put operation.
- textFile = true if text file, false if binary file. Must be true
- for get listing operations.
- data = pointer to handle. For put operations, the handle
- contains the data to put.
-
- Exit: function result = result code.
- *data = For get operations, contains the data.
- ----------------------------------------------------------------------------*/
-
- static OSErr GetOrPutFileOrListing (FtpStreamRef stream, char *path, char *cmd,
- Boolean get, Boolean textFile, Handle *data)
- {
- TStreamHandle s;
- NetStreamRef controlStream, dataStream;
- unsigned long myAddr;
- unsigned short port;
- CStr255 command, response;
- long responseCode;
- OSErr err = noErr;
- char *p;
- short commaCount;
-
- s = (TStreamHandle)stream;
- controlStream = (**s).netStream;
-
- if (textFile != (**s).textMode) {
- err = NetCommand(controlStream,
- textFile ? "TYPE A" : "TYPE I",
- &responseCode, response);
- if (err != noErr) goto exit1;
- if (responseCode != 200) return ftpServerErr;
- (**s).textMode = textFile;
- }
-
- if ((**s).passive) {
-
- /* Passive mode: Send PASV command. */
-
- err = NetCommand(controlStream, "PASV", &responseCode, response);
- if (err != noErr) goto exit1;
- if (responseCode != 227) return ftpServerErr;
-
- /* Parse the port number from the response to the PASV command:
- 227 Entering Passive Mode (h1,h2,h3,h4,p1,p2). */
-
- p = response;
- while (*p != 0 && *p != ')') p++;
- if (*p == 0) return ftpServerErr;
- commaCount = 0;
- while (true) {
- while (p > response && *p != ',') p--;
- if (p <= response) return ftpServerErr;
- commaCount++;
- if (commaCount == 2) break;
- p--;
- }
- p++;
- port = atoi(p);
- while (*p != ',') p++;
- p++;
- port = (port << 8) | atoi(p);
-
- /* Connect to server's listening data stream port. */
-
- err = NetOpen((**s).addr, port, false, &dataStream, &responseCode, response);
- if (err != noErr) goto exit1;
-
- } else {
-
- /* Active mode: Establish listening data stream and send PORT command. */
-
- err = NetGetMyAddr(&myAddr);
- if (err != noErr) goto exit1;
-
- err = NetFTPDataPassiveOpen(&port, &dataStream);
- if (err != noErr) goto exit1;
-
- sprintf(command, "PORT %hu,%hu,%hu,%hu,%hu,%hu",
- (unsigned short)((myAddr>>24)&0xff),
- (unsigned short)((myAddr>>16)&0xff),
- (unsigned short)((myAddr>>8)&0xff),
- (unsigned short)(myAddr&0xff),
- (unsigned short)((port>>8)&0xff),
- (unsigned short)(port&0xff));
- err = NetCommand(controlStream, command, &responseCode, response);
- if (err != noErr) goto exit2;
- if (responseCode != 200) goto exit3;
-
- }
-
- (**s).dataStream = dataStream;
- (**s).get = get;
-
- sprintf(command, "%s %.250s", cmd, path);
- err = NetCommand(controlStream, command, &responseCode, response);
- if (err != noErr) goto exit2;
- if (responseCode != 150 && responseCode != 125) goto exit3;
-
- if (!(**s).passive) {
-
- /* Active mode: Wait for server to open its end of the data stream. */
-
- err = NetFTPDataWaitForConnection(dataStream);
- if (err != noErr) goto exit1;
-
- }
-
- if (get) {
- err = NetGetFTPData(dataStream, textFile, data);
- } else {
- err = NetPutFTPData(dataStream, textFile, *data);
- }
- if (err != noErr) goto exit1;
-
- (**s).dataStream = nil;
-
- err = NetFTPDataClose(dataStream);
- if (err != noErr) goto exit1;
-
- err = NetGetExtraResponse(controlStream, &responseCode, response);
- if (err != noErr) goto exit4;
- if (responseCode != 226) return ftpServerErr;
-
- return noErr;
-
- exit1:
-
- NetClose(controlStream);
- MyDisposeHandle(s);
- return err;
-
- exit2:
-
- NetFTPDataClose(dataStream);
- MyDisposeHandle(s);
- return err;
-
- exit3:
-
- NetFTPDataClose(dataStream);
- (**s).dataStream = nil;
- return ftpServerErr;
-
- exit4:
-
- MyDisposeHandle(s);
- return err;
- }
-
-
-
- /*----------------------------------------------------------------------------
- FtpGetFile
-
- Get a file from an FTP server.
-
- Entry: stream = stream reference.
- path = file path.
- textFile = true if text file, false if binary file.
-
- Exit: function result = result code.
- data = handle to data.
- ----------------------------------------------------------------------------*/
-
- OSErr FtpGetFile (FtpStreamRef stream, char *path, Boolean textFile, Handle *data)
- {
- return GetOrPutFileOrListing(stream, path, "RETR", true, textFile, data);
- }
-
-
-
- /*----------------------------------------------------------------------------
- FtpGetListing
-
- Get a directory listing from an FTP server.
-
- Entry: stream = stream reference.
- path = directory or file path.
-
- Exit: function result = result code.
- data = handle to listing.
- ----------------------------------------------------------------------------*/
-
- OSErr FtpGetListing (FtpStreamRef stream, char *path, Handle *data)
- {
- return GetOrPutFileOrListing(stream, path, "LIST", true, true, data);
- }
-
-
-
- /*----------------------------------------------------------------------------
- FtpPutFile
-
- Send a file to an FTP server.
-
- Entry: stream = stream reference.
- path = file path.
- textFile = true if text file, false if binary file.
- data = handle to data.
-
- Exit: function result = result code.
- ----------------------------------------------------------------------------*/
-
- OSErr FtpPutFile (FtpStreamRef stream, char *path, Boolean textFile, Handle data)
- {
- return GetOrPutFileOrListing(stream, path, "STOR", false, textFile, &data);
- }
-
-
-
- /*----------------------------------------------------------------------------
- FtpDeleteFile
-
- Delete a file on an FTP server.
-
- Entry: stream = stream reference.
- path = file path.
-
- Exit: function result = result code.
- ----------------------------------------------------------------------------*/
-
- OSErr FtpDeleteFile (FtpStreamRef stream, char *path)
- {
- TStreamHandle s;
- NetStreamRef controlStream;
- CStr255 command, response;
- long responseCode;
- OSErr err = noErr;
-
- s = (TStreamHandle)stream;
- controlStream = (**s).netStream;
-
- sprintf(command, "DELE %.250s", path);
- err = NetCommand(controlStream, command, &responseCode, response);
- if (err != noErr) goto exit;
- if (responseCode != 250) return ftpServerErr;
-
- return noErr;
-
- exit:
-
- MyDisposeHandle(s);
- return err;
- }
-
-
-
- /*----------------------------------------------------------------------------
- FtpGetServerErrInfo
-
- Get server error information.
-
- Entry: stream = stream reference.
-
- Exit: *serverErrInfo = server error information.
- ----------------------------------------------------------------------------*/
-
- void FtpGetServerErrInfo (FtpStreamRef stream, NetServerErrInfo *serverErrInfo)
- {
- TStreamHandle s;
-
- s = (TStreamHandle)stream;
- NetGetServerErrInfo((**s).netStream, serverErrInfo);
- }
-
-
-
- /*----------------------------------------------------------------------------
- FtpNumBytesTransferred
-
- Get the number of data bytes transferred so far.
-
- Entry: stream = stream reference.
-
- Exit: function result = number of bytes transferred, or 0 if no data
- stream open.
- ----------------------------------------------------------------------------*/
-
- long FtpNumBytesTransferred (FtpStreamRef stream)
- {
- TStreamHandle s;
- NetStreamRef dataStream;
- long bytesIn, bytesOut;
-
- s = (TStreamHandle)stream;
- dataStream = (**s).dataStream;
- if (dataStream == nil) return 0;
- NetGetStreamStats(dataStream, &bytesIn, &bytesOut);
- return (**s).get ? bytesIn : bytesOut;
- }
-